page.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. "use client";
  2. import { fetchApi, fetchFile } from "@/app/_modules/func";
  3. import {
  4. CaretDownOutlined,
  5. CheckOutlined,
  6. CloseOutlined,
  7. DeleteOutlined,
  8. ExclamationCircleFilled,
  9. EyeOutlined,
  10. PlusOutlined,
  11. ReloadOutlined,
  12. SearchOutlined,
  13. KeyOutlined,
  14. LoadingOutlined,
  15. CloudUploadOutlined,
  16. FileAddOutlined,
  17. } from "@ant-design/icons";
  18. import type {
  19. ProColumns,
  20. ProFormInstance,
  21. ActionType,
  22. } from "@ant-design/pro-components";
  23. import {
  24. ModalForm,
  25. PageContainer,
  26. ProCard,
  27. ProForm,
  28. ProFormRadio,
  29. ProFormSelect,
  30. ProFormText,
  31. ProFormTextArea,
  32. ProFormTreeSelect,
  33. ProTable,
  34. } from "@ant-design/pro-components";
  35. import type { TreeDataNode, MenuProps, UploadProps, GetProp } from "antd";
  36. import {
  37. Button,
  38. Col,
  39. Flex,
  40. Input,
  41. message,
  42. Modal,
  43. Row,
  44. Space,
  45. Spin,
  46. Switch,
  47. Tree,
  48. Dropdown,
  49. Form,
  50. Upload,
  51. Typography,
  52. Checkbox,
  53. Tag,
  54. } from "antd";
  55. import { useRouter } from "next/navigation";
  56. import {
  57. faDownload,
  58. faPenToSquare,
  59. faToggleOff,
  60. faToggleOn,
  61. faUpload,
  62. faUsers,
  63. faCheck,
  64. faXmark,
  65. } from "@fortawesome/free-solid-svg-icons";
  66. import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
  67. import { useEffect, useMemo, useRef, useState } from "react";
  68. type FileType = Parameters<GetProp<UploadProps, "beforeUpload">>[0];
  69. const { Dragger } = Upload;
  70. export type OptionType = {
  71. label: string;
  72. value: string | number;
  73. };
  74. export default function RoleAuth({ params }: { params: { roleid: string } }) {
  75. const { push } = useRouter();
  76. const roleId = params.roleid;
  77. //表格列定义
  78. const columns: ProColumns[] = [
  79. {
  80. title: "用户名称",
  81. dataIndex: "userName",
  82. order: 2,
  83. },
  84. {
  85. title: "用户昵称",
  86. dataIndex: "nickName",
  87. search: false,
  88. },
  89. {
  90. title: "邮箱",
  91. dataIndex: "email",
  92. search: false,
  93. },
  94. {
  95. title: "手机号",
  96. dataIndex: "phonenumber",
  97. order: 1,
  98. },
  99. {
  100. title: "状态",
  101. dataIndex: "status",
  102. search: false,
  103. valueEnum: {
  104. 0: {
  105. text: "正常",
  106. status: "0",
  107. },
  108. 1: {
  109. text: "停用",
  110. status: "1",
  111. },
  112. },
  113. render: (text, record) => {
  114. return (
  115. <Space>
  116. <Tag
  117. color={record.status == 0 ? "green" : "red"}
  118. icon={
  119. record.status == 0 ? (
  120. <FontAwesomeIcon icon={faCheck} />
  121. ) : (
  122. <FontAwesomeIcon icon={faXmark} />
  123. )
  124. }
  125. >
  126. {text}
  127. </Tag>
  128. </Space>
  129. );
  130. },
  131. },
  132. {
  133. title: "创建时间",
  134. dataIndex: "createTime",
  135. valueType: "dateTime",
  136. search: false,
  137. },
  138. {
  139. title: "操作",
  140. key: "option",
  141. search: false,
  142. render: (_, record) => {
  143. if (record.userId != 1)
  144. return [
  145. <Button
  146. key="deleteBtn"
  147. type="link"
  148. danger
  149. icon={<DeleteOutlined />}
  150. onClick={() => onClickRemoveAuth(record)}
  151. >
  152. 取消授权
  153. </Button>,
  154. ];
  155. },
  156. },
  157. ];
  158. //未分配授权用户列定义
  159. const unAllocateColumns: ProColumns[] = [
  160. {
  161. title: "用户名称",
  162. dataIndex: "userName",
  163. order: 2,
  164. },
  165. {
  166. title: "用户昵称",
  167. dataIndex: "nickName",
  168. search: false,
  169. },
  170. {
  171. title: "邮箱",
  172. dataIndex: "email",
  173. search: false,
  174. },
  175. {
  176. title: "手机号",
  177. dataIndex: "phonenumber",
  178. order: 1,
  179. },
  180. {
  181. title: "状态",
  182. dataIndex: "status",
  183. search: false,
  184. valueEnum: {
  185. 0: {
  186. text: "正常",
  187. status: "0",
  188. },
  189. 1: {
  190. text: "停用",
  191. status: "1",
  192. },
  193. },
  194. render: (text, record) => {
  195. return (
  196. <Space>
  197. <Tag
  198. color={record.status == 0 ? "green" : "red"}
  199. icon={
  200. record.status == 0 ? (
  201. <FontAwesomeIcon icon={faCheck} />
  202. ) : (
  203. <FontAwesomeIcon icon={faXmark} />
  204. )
  205. }
  206. >
  207. {text}
  208. </Tag>
  209. </Space>
  210. );
  211. },
  212. },
  213. {
  214. title: "创建时间",
  215. dataIndex: "createTime",
  216. valueType: "dateTime",
  217. search: false,
  218. },
  219. ];
  220. //查询角色授权数据
  221. const getRoleAllocate = async (params: any, sorter: any, filter: any) => {
  222. const searchParams = {
  223. roleId: roleId,
  224. pageNum: params.current,
  225. ...params,
  226. };
  227. delete searchParams.current;
  228. const queryParams = new URLSearchParams(searchParams);
  229. Object.keys(sorter).forEach((key) => {
  230. queryParams.append("orderByColumn", key);
  231. if (sorter[key] === "ascend") {
  232. queryParams.append("isAsc", "ascending");
  233. } else {
  234. queryParams.append("isAsc", "descending");
  235. }
  236. });
  237. const body = await fetchApi(
  238. `/api/system/role/authUser/allocatedList?${queryParams}`,
  239. push
  240. );
  241. if (body !== undefined) {
  242. return body;
  243. }
  244. };
  245. //查询角色未授权数据
  246. const getRoleUnallocate = async (params: any, sorter: any, filter: any) => {
  247. const searchParams = {
  248. roleId: roleId,
  249. pageNum: params.current,
  250. ...params,
  251. };
  252. delete searchParams.current;
  253. const queryParams = new URLSearchParams(searchParams);
  254. Object.keys(sorter).forEach((key) => {
  255. queryParams.append("orderByColumn", key);
  256. if (sorter[key] === "ascend") {
  257. queryParams.append("isAsc", "ascending");
  258. } else {
  259. queryParams.append("isAsc", "descending");
  260. }
  261. });
  262. const body = await fetchApi(
  263. `/api/system/role/authUser/unallocatedList?${queryParams}`,
  264. push
  265. );
  266. if (body !== undefined) {
  267. return body;
  268. }
  269. };
  270. //取消授权按钮是否可用,选中行时才可用
  271. const [rowCanRemoveAuth, setCanRemoveAuth] = useState(false);
  272. //点击批量取消授权按钮
  273. const onClickBatchRemoveAuth = () => {
  274. Modal.confirm({
  275. title: "系统提示",
  276. icon: <ExclamationCircleFilled />,
  277. content: `确定要取消选中用户的角色授权吗?`,
  278. onOk() {
  279. executeBatchRemoveRoleAuth();
  280. },
  281. onCancel() {},
  282. });
  283. };
  284. //点击取消授权按钮
  285. const onClickRemoveAuth = (record: any) => {
  286. const userId = record.userId;
  287. Modal.confirm({
  288. title: "系统提示",
  289. icon: <ExclamationCircleFilled />,
  290. content: `确定要取消用户“${record.userName}”的角色授权吗?`,
  291. onOk() {
  292. executeRemoveRoleAuth(userId);
  293. },
  294. onCancel() {},
  295. });
  296. };
  297. //执行批量取消用户角色授权
  298. const executeBatchRemoveRoleAuth = async () => {
  299. const data = {
  300. roleId: roleId,
  301. userIds: selectedRowKeys.join(","),
  302. };
  303. const body = await fetchApi(
  304. `/api/system/role/authUser/cancelAll?${new URLSearchParams(data)}`,
  305. push,
  306. {
  307. method: "PUT",
  308. }
  309. );
  310. if (body !== undefined) {
  311. if (body.code == 200) {
  312. message.success("批量取消授权成功");
  313. } else {
  314. message.error(body.msg);
  315. }
  316. setSelectedRowKeys([]);
  317. //刷新表格
  318. if (actionRef.current) {
  319. actionRef.current.reload();
  320. }
  321. }
  322. };
  323. //执行取消用户角色授权
  324. const executeRemoveRoleAuth = async (userId: any) => {
  325. const data = {
  326. roleId: roleId,
  327. userId: userId,
  328. };
  329. const body = await fetchApi("/api/system/role/authUser/cancel", push, {
  330. method: "PUT",
  331. headers: {
  332. "Content-Type": "application/json",
  333. },
  334. body: JSON.stringify(data),
  335. });
  336. if (body !== undefined) {
  337. if (body.code == 200) {
  338. message.success("取消授权成功");
  339. } else {
  340. message.error(body.msg);
  341. }
  342. //刷新表格
  343. if (actionRef.current) {
  344. actionRef.current.reload();
  345. }
  346. }
  347. };
  348. //选中行操作
  349. const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
  350. const rowSelection = {
  351. onChange: (newSelectedRowKeys: React.Key[]) => {
  352. setSelectedRowKeys(newSelectedRowKeys);
  353. setCanRemoveAuth(newSelectedRowKeys && newSelectedRowKeys.length > 0);
  354. },
  355. };
  356. //未授权用户选中行操作
  357. const [selectedRowKeysUnallocate, setSelectedRowKeysUnallocate] = useState<
  358. React.Key[]
  359. >([]);
  360. const rowSelectionUnallocate = {
  361. onChange: (newSelectedRowKeys: React.Key[]) => {
  362. setSelectedRowKeysUnallocate(newSelectedRowKeys);
  363. },
  364. };
  365. //是否展示分配用户对话框
  366. const [showUnallocateModal, setShowUnallocateModal] = useState(false);
  367. //展示分配用户对话框
  368. const onClickShowModal = () => {
  369. if (unallocateActionRef.current) {
  370. unallocateActionRef.current.reload();
  371. }
  372. setShowUnallocateModal(true);
  373. };
  374. //确认分配新的用户
  375. const confirmAddUnallocate = async () => {
  376. const data = {
  377. roleId: roleId,
  378. userIds: selectedRowKeysUnallocate.join(","),
  379. };
  380. const body = await fetchApi(
  381. `/api/system/role/authUser/selectAll?${new URLSearchParams(data)}`,
  382. push,
  383. {
  384. method: "PUT",
  385. }
  386. );
  387. if (body !== undefined) {
  388. if (body.code == 200) {
  389. message.success(body.msg);
  390. } else {
  391. message.error(body.msg);
  392. }
  393. }
  394. setSelectedRowKeysUnallocate([]);
  395. if (unallocateActionRef.current) {
  396. unallocateActionRef.current.reload();
  397. }
  398. console.log(selectedRowKeysUnallocate);
  399. if (actionRef.current) {
  400. actionRef.current.reload();
  401. }
  402. setShowUnallocateModal(false);
  403. };
  404. //取消分配用户
  405. const cancelAddUnallocate = () => {
  406. setShowUnallocateModal(false);
  407. };
  408. //搜索栏显示状态
  409. const [showSearch, setShowSearch] = useState(true);
  410. //action对象引用
  411. const actionRef = useRef<ActionType>();
  412. //表单对象引用
  413. const formRef = useRef<ProFormInstance>();
  414. //未分配用户列表action对象引用
  415. const unallocateActionRef = useRef<ActionType>();
  416. //当前默认条数
  417. const defaultPageSize = 10;
  418. return (
  419. <PageContainer
  420. header={{
  421. title: "分配用户",
  422. onBack(e) {
  423. push("/system/role");
  424. },
  425. }}
  426. >
  427. <ProTable
  428. formRef={formRef}
  429. rowKey="userId"
  430. rowSelection={{
  431. selectedRowKeys,
  432. ...rowSelection,
  433. }}
  434. columns={columns}
  435. request={async (params: any, sorter: any, filter: any) => {
  436. // 表单搜索项会从 params 传入,传递给后端接口。
  437. const data = await getRoleAllocate(params, sorter, filter);
  438. if (data !== undefined) {
  439. return Promise.resolve({
  440. data: data.rows,
  441. success: true,
  442. total: data.total,
  443. });
  444. }
  445. return Promise.resolve({
  446. data: [],
  447. success: true,
  448. });
  449. }}
  450. pagination={{
  451. defaultPageSize: defaultPageSize,
  452. showQuickJumper: true,
  453. showSizeChanger: true,
  454. }}
  455. search={
  456. showSearch
  457. ? {
  458. defaultCollapsed: false,
  459. searchText: "搜索",
  460. }
  461. : false
  462. }
  463. dateFormatter="string"
  464. actionRef={actionRef}
  465. toolbar={{
  466. actions: [
  467. <Button icon={<PlusOutlined />} key="allocate" type="primary" onClick={onClickShowModal}>
  468. 添加用户
  469. </Button>,
  470. <Button
  471. key="unallocate"
  472. danger
  473. icon={<DeleteOutlined />}
  474. disabled={!rowCanRemoveAuth}
  475. onClick={() => onClickBatchRemoveAuth()}
  476. >
  477. 批量取消授权
  478. </Button>,
  479. ],
  480. settings: [
  481. {
  482. key: "switch",
  483. icon: showSearch ? (
  484. <FontAwesomeIcon icon={faToggleOn} />
  485. ) : (
  486. <FontAwesomeIcon icon={faToggleOff} />
  487. ),
  488. tooltip: showSearch ? "隐藏搜索栏" : "显示搜索栏",
  489. onClick: (key: string | undefined) => {
  490. setShowSearch(!showSearch);
  491. },
  492. },
  493. {
  494. key: "refresh",
  495. tooltip: "刷新",
  496. icon: <ReloadOutlined />,
  497. onClick: (key: string | undefined) => {
  498. if (actionRef.current) {
  499. actionRef.current.reload();
  500. }
  501. },
  502. },
  503. ],
  504. }}
  505. />
  506. <Modal
  507. title={`选择用户`}
  508. width={1000}
  509. open={showUnallocateModal}
  510. onOk={confirmAddUnallocate}
  511. onCancel={cancelAddUnallocate}
  512. >
  513. <ProTable
  514. rowKey="userId"
  515. rowSelection={{
  516. selectedRowKeys: selectedRowKeysUnallocate,
  517. ...rowSelectionUnallocate,
  518. }}
  519. columns={unAllocateColumns}
  520. request={async (params: any, sorter: any, filter: any) => {
  521. // 表单搜索项会从 params 传入,传递给后端接口。
  522. const data = await getRoleUnallocate(params, sorter, filter);
  523. if (data !== undefined) {
  524. return Promise.resolve({
  525. data: data.rows,
  526. success: true,
  527. total: data.total,
  528. });
  529. }
  530. return Promise.resolve({
  531. data: [],
  532. success: true,
  533. });
  534. }}
  535. pagination={{
  536. defaultPageSize: defaultPageSize,
  537. showQuickJumper: true,
  538. showSizeChanger: true,
  539. }}
  540. search={
  541. showSearch
  542. ? {
  543. defaultCollapsed: false,
  544. searchText: "搜索",
  545. }
  546. : false
  547. }
  548. dateFormatter="string"
  549. actionRef={unallocateActionRef}
  550. toolbar={{
  551. actions: [],
  552. settings: [],
  553. }}
  554. />
  555. </Modal>
  556. </PageContainer>
  557. );
  558. }